use anyhow::Result;
use chrono::{DateTime, Days, FixedOffset, Utc};
use serde::{Deserialize, Serialize};
use std::fmt::format;
//generate 10000 users and run them in a tx,repeat 500 times
use chrono_tz::{America::New_York, Asia::Shanghai};
use fake::{
    Dummy, Fake, Faker, Rng,
    faker::{chrono::en::DateTimeBetween, internet::en::SafeEmail, name::zh_cn::Name},
};
use nanoid::alphabet::SAFE;
use nanoid::nanoid;
use sqlx::MySqlPool;

#[derive(Debug, Clone, Serialize, Deserialize, Dummy)]
pub struct UserStat {
    pub id: u64,
    #[dummy(faker = "UniqueEmail")]
    pub email: String,
    #[serde(
        serialize_with = "user_stat::common::serialize_utc",
        deserialize_with = "user_stat::common::deserialize_utc"
    )]
    #[dummy(faker = "DateTimeBetween(before(30), now())")]
    pub last_visited_time: DateTime<Utc>,
    #[serde(
        serialize_with = "user_stat::common::serialize_utc",
        deserialize_with = "user_stat::common::deserialize_utc"
    )]
    #[dummy(faker = "DateTimeBetween(before(90), now())")]
    pub last_watched_time: DateTime<Utc>,
    #[dummy(faker = "IntList(50, 400000, 100000)")]
    pub recent_watched: String,
    #[dummy(faker = "IntList(50, 400000, 100000)")]
    pub viewed_but_not_started_in: String,
    #[dummy(faker = "IntList(50, 400000, 100000)")]
    pub started_but_not_finished: String,
    pub finished: String,
    #[serde(
        serialize_with = "user_stat::common::serialize_utc",
        deserialize_with = "user_stat::common::deserialize_utc"
    )]
    #[dummy(faker = "DateTimeBetween(before(45), now())")]
    pub last_email_notification: DateTime<Utc>,
    #[serde(
        serialize_with = "user_stat::common::serialize_utc",
        deserialize_with = "user_stat::common::deserialize_utc"
    )]
    #[dummy(faker = "DateTimeBetween(before(15), now())")]
    pub last_in_app_notification: DateTime<Utc>,
    #[serde(
        serialize_with = "user_stat::common::serialize_utc",
        deserialize_with = "user_stat::common::deserialize_utc"
    )]
    pub last_sms_notification: DateTime<Utc>,
    #[serde(
        serialize_with = "user_stat::common::serialize_utc",
        deserialize_with = "user_stat::common::deserialize_utc"
    )]
    #[dummy(faker = "DateTimeBetween(before(90), now())")]
    pub created_at: DateTime<Utc>,
    #[serde(
        serialize_with = "user_stat::common::serialize_utc",
        deserialize_with = "user_stat::common::deserialize_utc"
    )]
    pub updated_at: DateTime<Utc>,
}

fn before(days: u64) -> DateTime<Utc> {
    Utc::now().checked_sub_days(Days::new(days)).unwrap()
}
fn now() -> DateTime<Utc> {
    Utc::now()
}

pub struct UniqueEmail;

impl Dummy<UniqueEmail> for String {
    fn dummy_with_rng<R: Rng + ?Sized>(_: &UniqueEmail, rng: &mut R) -> String {
        let email: String = SafeEmail().fake_with_rng(rng);
        let id = nanoid!(8, &SAFE);
        let at = email.find('@').unwrap();
        let email = format!("{}.{}{}", &email[..at], id, &email[at..]);
        email
    }
}

struct IntList(pub i32, pub i32, pub i32);

impl Dummy<IntList> for String {
    fn dummy_with_rng<R: Rng + ?Sized>(v: &IntList, rng: &mut R) -> String {
        let (max, start, len) = (v.0, v.1, v.2);
        let size = rng.random_range(0..max);
        let vec = (0..size)
            .map(|_| rng.random_range(start..start + len))
            .collect::<Vec<i32>>();
        serde_json::to_string(&vec).unwrap()
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    let utc_now = Utc::now();

    // 转换到东八区（北京时间）
    let beijing_offset = FixedOffset::east_opt(8 * 3600).unwrap();
    let beijing_time = utc_now.with_timezone(&beijing_offset);
    println!("北京时间: {}", beijing_time);

    let utc_now = Utc::now();

    // 转换到上海（自动处理夏令时）
    let shanghai_time = utc_now.with_timezone(&Shanghai);
    println!("上海时间: {}", shanghai_time);

    // 转换到纽约（自动处理DST）
    let ny_time = utc_now.with_timezone(&New_York);
    println!("纽约时间: {}", ny_time);

    let f: UserStat = Faker.fake();
    println!("{:?}", serde_json::to_string(&f));

    let id = nanoid!(32, &SAFE);
    println!("id:{id}");
    let mysql = MySqlPool::connect("mysql://root:123456@127.0.0.1:3306/chat").await?;

    for _i in 0..10000 {
        let f: UserStat = Faker.fake();
        sqlx::query(r#"
            insert into user_stats
                (email,last_visited_time,last_watched_time,recent_watched,viewed_but_not_started_in,started_but_not_finished,finished,last_email_notification,last_in_app_notification,last_sms_notification)
            values
                (?,?,?,?,?,?,?,?,?,?)
            "#)
            .bind(f.email)
            .bind(f.last_visited_time)
            .bind(f.last_watched_time)
            .bind(f.recent_watched)
            .bind(f.viewed_but_not_started_in)
            .bind(f.started_but_not_finished)
            .bind(f.finished)
            .bind(f.last_email_notification)
            .bind(f.last_in_app_notification)
            .bind(f.last_sms_notification)
            .execute(&mysql).await?;
    }
    Ok(())
}
